/* 
 * File:   FeatureVectors.cpp
 * Author: ungerma
 * 
 * Created on 04. August 2014, 15:23
 */

#include "FeatureVectors.h"
#include "MPEG7Settings.h"

using namespace std;

FeatureVectors::FeatureVectors() {
   
    this->settings = HOGSettings();
           
}

FeatureVectors::FeatureVectors(HOGSettings settings) {
   
    this->settings = settings;
           
}

void FeatureVectors::computeMPEG7(vector<float> &fv, string mpeg7List, 
    string mpeg7Path, string mpegScd, string mpegEhd) {
    
    MPEG7Settings s;
    
    string cmd = mpeg7Path +  " SCD " + s.getSCDSize() + " " + mpeg7List + " " + 
            mpegScd + " 2> NUL";
    cout << cmd << endl;
    int i = system(cmd.c_str());
    if(i != 0) {
        throw runtime_error("Could not run mpeg7 scd command");
    }
    readAndBinValues(mpegScd, s.getSCDBins(), fv, s.getMinSCD(), s.getMaxSCD());
    
    // Sic! EHD has fixed size 80.
    cmd = mpeg7Path +  " EHD " + mpeg7List + " " + mpegEhd + " 2> NUL";
    cout << cmd << endl;
    i = system(cmd.c_str());
    if(i != 0) {
        throw runtime_error("Could not run mpeg7 scd command");
    }
    readAndBinValues(mpegEhd, s.getEHDBins(), fv, s.getMinEHD(), s.getMaxEHD());
    
}

void FeatureVectors::readAndBinValues(string filename, int numBins,
        vector<float> &fv, int minT, int maxT) {

    vector<float> values, binnedValues;
    ifstream file;
    string line;
    int t, first = 1;
    file.open(filename.c_str());
    if(!file.is_open()) {
         throw runtime_error("Could not open " + filename + " for writing");
    }    
    
    while(file >> line) {
        if(first) {
            first = 0;
        }
        else {
            t = atoi(line.c_str());
            values.push_back(t);
        } 
    }
    
    file.close();
    if(file.is_open()) {
        throw runtime_error("Could not close " + filename);
    }
    
    for(int i=0; i < numBins * values.size(); ++i) {
        binnedValues.push_back(0);
    }
    float binSize = (maxT - minT + 1) / (float) numBins;
    
    // Not a histogram, but binning, e.g. 8, 2 =
    // 1 0 0 0 0 0 0 0, 0 0 0 0 0 0 1 0
    for(int i=0; i < values.size(); ++i) {    
        for(int b = 0; b < numBins; ++b) {
            
            int val = values.at(i);
            if(val >= minT + b * binSize && val < minT + (b+1) * binSize) {
                binnedValues.at(i*numBins+b) = 1;
                /*cout << "Put " << val << " in bin# " << b << "/" << 
                        (i*numBins+b) << "(" <<
                        (minT + b * binSize)  << ", " << 
                        (minT + (b+1) * binSize) << "]" << endl;*/
            }
            
        }
    }

    for(int i=0; i < binnedValues.size(); ++i) {
        fv.push_back(binnedValues.at(i));
    }           
}

void FeatureVectors::calculateAndSerialize(SingleImage si, float clz, 
        string filename, string mpeg7List, string mpeg7Path, string mpegScd, 
        string mpegEhd) {
    
    HOGDescriptor hog = settings.build();
    vector<Point> locations;
    int noFv = 0;
    
    ofstream file;
    file.open(filename.c_str(), file.app);
    if(!file.is_open()) {
         throw runtime_error("Could not open " + filename + " for writing");
    }
 
    if(si.hasWindows()) {
        
        for(int j = 0 ; j < si.getWindows()->size() ; ++j) {
            
            vector<float> fv;
            hog.compute(si.getWindows()->at(j).getData(), fv, 
                    settings.getWinStride(), settings.getPadding(), locations);
            computeMPEG7(fv, mpeg7List, mpeg7Path, mpegScd, mpegEhd);
            fv.push_back(clz);
            serialize(fv, file, si.getFilename());
            ++noFv;
            
        }
        
    }
    else {

       vector<float> fv;
       hog.compute(si.getData(), fv, settings.getWinStride(), 
               settings.getPadding(), locations);
       computeMPEG7(fv, mpeg7List, mpeg7Path, mpegScd, mpegEhd);
       fv.push_back(clz);
       serialize(fv, file, si.getFilename());
       ++noFv;
       
    }
    
    file.close();
    if(file.is_open()) {
        throw runtime_error("Could not close " + filename);
    }
    cout << "Serialized " << noFv << " feature vector(s) " << endl;

}

void FeatureVectors::serialize(vector<float> fv, ofstream& file, string filename) {
    
    /* Hog compute is already normalized.
    // Normalize values.
    float max = *std::max_element(fv.begin(), fv.end());
    cout << "Normalizing to " << max << endl;
    for(int j = 0; j < fv.size(); ++j) {
        fv.at(j) = fv.at(j) / max;
    }
    */
    
    // Reverse to put class label at the front
    for(int j = fv.size() - 1, n; j >= 0  ; --j) {

        n = - j + fv.size() - 1;
        float val = fv.at(j);
        if(j == fv.size() - 1) {
            file << val << ' ';
        }
        else {
            file << n << ':' << val << ' ';
        }

    }
    // Put original file in name.
    file << " #" << filename;
    file << endl;

}
